/*
 * Copyright (C) 2004 NNL Technology AB
 * Visit www.infonode.net for information about InfoNode(R) 
 * products and how to contact NNL Technology AB.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
 * MA 02111-1307, USA.
 */

// $Id: SplitWindow.java,v 1.50 2007/01/28 21:25:10 jesper Exp $
package com.supermap.desktop.ui.docking;

import net.infonode.gui.ComponentUtil;
import net.infonode.gui.SimpleSplitPane;
import net.infonode.gui.SimpleSplitPaneListener;
import net.infonode.gui.panel.BaseContainerUtil;
import net.infonode.properties.propertymap.PropertyMap;
import net.infonode.util.Direction;

import javax.swing.*;

import com.supermap.desktop.ui.docking.drop.InteriorDropInfo;
import com.supermap.desktop.ui.docking.drop.SplitDropInfo;
import com.supermap.desktop.ui.docking.internal.ReadContext;
import com.supermap.desktop.ui.docking.internal.WindowAncestors;
import com.supermap.desktop.ui.docking.internal.WriteContext;
import com.supermap.desktop.ui.docking.internalutil.DropAction;
import com.supermap.desktop.ui.docking.model.SplitWindowItem;
import com.supermap.desktop.ui.docking.model.ViewReader;
import com.supermap.desktop.ui.docking.model.ViewWriter;
import com.supermap.desktop.ui.docking.properties.SplitWindowProperties;

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * A window with a split pane that contains two child windows.
 *
 * @author $Author: jesper $
 * @version $Revision: 1.50 $
 */
public class SplitWindow extends DockingWindow {
	private SimpleSplitPane splitPane;
	private DockingWindow leftWindow;
	private DockingWindow rightWindow;

	/**
	 * Creates a split window.
	 *
	 * @param horizontal
	 *            true if the split is horizontal
	 */
	public SplitWindow(boolean horizontal) {
		this(horizontal, null, null);
	}

	/**
	 * Creates a split window with with the given child windows.
	 *
	 * @param horizontal
	 *            true if the split is horizontal
	 * @param leftWindow
	 *            the left/upper window
	 * @param rightWindow
	 *            the right/lower window
	 */
	public SplitWindow(boolean horizontal, DockingWindow leftWindow, DockingWindow rightWindow) {
		this(horizontal, 0.5f, leftWindow, rightWindow);
	}

	/**
	 * Creates a split window with with the given child windows.
	 *
	 * @param horizontal
	 *            true if the split is horizontal
	 * @param dividerLocation
	 *            the divider location, 0 - 1
	 * @param leftWindow
	 *            the left/upper window
	 * @param rightWindow
	 *            the right/lower window
	 */
	public SplitWindow(boolean horizontal, float dividerLocation, DockingWindow leftWindow, DockingWindow rightWindow) {
		this(horizontal, dividerLocation, leftWindow, rightWindow, null);
	}

	protected SplitWindow(boolean horizontal, float dividerLocation, DockingWindow leftWindow, DockingWindow rightWindow, SplitWindowItem windowItem) {
		super(windowItem == null ? new SplitWindowItem() : windowItem);

		splitPane = new SimpleSplitPane(horizontal);
		BaseContainerUtil.setForcedOpaque(splitPane, false);
		// splitPane.setForcedOpaque(false);
		splitPane.addListener(new SimpleSplitPaneListener() {
			public void dividerLocationChanged(SimpleSplitPane simpleSplitPane) {
				((SplitWindowItem) getWindowItem()).setDividerLocation(simpleSplitPane.getDividerLocation());
			}
		});
		setComponent(splitPane);
		setWindows(leftWindow, rightWindow);
		setHorizontal(horizontal);
		setDividerLocation(dividerLocation);
		splitPane.getDividerPanel().addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				if (e.isPopupTrigger()) {
					showPopupMenu(e);
				}
			}

			public void mouseReleased(MouseEvent e) {
				mousePressed(e);
			}
		});
		init();
	}

	/**
	 * Returns the property values for this split window.
	 *
	 * @return the property values for this split window
	 */
	public SplitWindowProperties getSplitWindowProperties() {
		return ((SplitWindowItem) getWindowItem()).getSplitWindowProperties();
	}

	/**
	 * Returns the left/upper child window.
	 *
	 * @return the left/upper child window
	 */
	public DockingWindow getLeftWindow() {
		return leftWindow;
	}

	/**
	 * Returns the right/lower child window.
	 *
	 * @return the right/lower child window
	 */
	public DockingWindow getRightWindow() {
		return rightWindow;
	}

	/**
	 * Sets the divider location as a fraction of this split window's size.
	 *
	 * @param dividerLocation
	 *            the divider location as a fraction of this split window's size
	 */
	public void setDividerLocation(float dividerLocation) {
		splitPane.setDividerLocation(dividerLocation);
	}

	/**
	 * Returns the divider location as a fraction of this split window's size.
	 *
	 * @return the divider location as a fraction of this split window's size
	 */
	public float getDividerLocation() {
		return splitPane.getDividerLocation();
	}

	/**
	 * Sets the child windows of this split window.
	 *
	 * @param leftWindow
	 *            the left/upper child window
	 * @param rightWindow
	 *            the right/lower child window
	 */
	public void setWindows(final DockingWindow leftWindow, final DockingWindow rightWindow) {
		if (leftWindow == getLeftWindow() && rightWindow == getRightWindow())
			return;

		optimizeAfter(null, new Runnable() {
			public void run() {
				WindowAncestors leftAncestors = leftWindow.storeAncestors();
				WindowAncestors rightAncestors = rightWindow.storeAncestors();

				DockingWindow lw = leftWindow.getContentWindow(SplitWindow.this);
				DockingWindow rw = rightWindow.getContentWindow(SplitWindow.this);
				lw.detach();
				rw.detach();

				if (getLeftWindow() != null)
					removeWindow(getLeftWindow());

				if (getRightWindow() != null)
					removeWindow(getRightWindow());

				SplitWindow.this.leftWindow = lw;
				SplitWindow.this.rightWindow = rw;
				splitPane.setComponents(lw, rw);
				addWindow(lw);
				addWindow(rw);

				if (getUpdateModel()) {
					addWindowItem(getLeftWindow(), -1);
					addWindowItem(getRightWindow(), -1);
					cleanUpModel();
				}

				leftWindow.notifyListeners(leftAncestors);
				rightWindow.notifyListeners(rightAncestors);
			}
		});
	}

	/**
	 * Returns true if this SplitWindow is a horizontal split, otherwise it's vertical.
	 *
	 * @return true if this SplitWindow is a horizontal split, otherwise it's vertical
	 * @since IDW 1.2.0
	 */
	public boolean isHorizontal() {
		return splitPane.isHorizontal();
	}

	/**
	 * Sets the split to horizontal or vertical.
	 *
	 * @param horizontal
	 *            if true the split is set to horizontal, otherwise vertical
	 * @since IDW 1.2.0
	 */
	public void setHorizontal(boolean horizontal) {
		splitPane.setHorizontal(horizontal);
		((SplitWindowItem) getWindowItem()).setHorizontal(horizontal);
	}

	protected void update() {
		splitPane.setDividerSize(getSplitWindowProperties().getDividerSize());
		splitPane.setContinuousLayout(getSplitWindowProperties().getContinuousLayoutEnabled());
		splitPane.setDividerDraggable(getSplitWindowProperties().getDividerLocationDragEnabled());
		splitPane.setDragIndicatorColor(getSplitWindowProperties().getDragIndicatorColor());
	}

	protected void optimizeWindowLayout() {
		DockingWindow parent = getWindowParent();

		if (parent != null && (getRightWindow() == null || getLeftWindow() == null)) {
			if (getRightWindow() == null && getLeftWindow() == null)
				parent.removeChildWindow(this);
			else {
				DockingWindow w = getRightWindow() == null ? getLeftWindow() : getRightWindow();
				parent.internalReplaceChildWindow(this, w.getBestFittedWindow(parent));
			}
		}
	}

	public DockingWindow getChildWindow(int index) {
		return getWindows()[index];
	}

	protected void rootChanged(RootWindow oldRoot, RootWindow newRoot) {
		super.rootChanged(oldRoot, newRoot);
		if (newRoot != null)
			splitPane.setHeavyWeightDragIndicator(newRoot.isHeavyweightSupported());
	}

	private DockingWindow[] getWindows() {
		return getLeftWindow() == null ? (getRightWindow() == null ? new DockingWindow[0] : new DockingWindow[] { getRightWindow() })
				: getRightWindow() == null ? new DockingWindow[] { getLeftWindow() } : new DockingWindow[] { getLeftWindow(), getRightWindow() };
	}

	public int getChildWindowCount() {
		return getWindows().length;
	}

	public Icon getIcon() {
		return getLeftWindow() == null ? (getRightWindow() == null ? null : getRightWindow().getIcon()) : getLeftWindow().getIcon();
	}

	protected void doReplace(DockingWindow oldWindow, DockingWindow newWindow) {
		if (getLeftWindow() == oldWindow) {
			leftWindow = newWindow;
			splitPane.setLeftComponent(newWindow);
		} else {
			rightWindow = newWindow;
			splitPane.setRightComponent(newWindow);
		}

		ComponentUtil.validate(splitPane);
	}

	protected void doRemoveWindow(DockingWindow window) {
		if (window == getLeftWindow()) {
			leftWindow = null;
			splitPane.setLeftComponent(null);
		} else {
			rightWindow = null;
			splitPane.setRightComponent(null);
		}
	}

	protected DockingWindow oldRead(ObjectInputStream in, ReadContext context) throws IOException {
		splitPane.setHorizontal(in.readBoolean());
		splitPane.setDividerLocation(in.readFloat());
		DockingWindow leftWindow = WindowDecoder.decodeWindow(in, context);
		DockingWindow rightWindow = WindowDecoder.decodeWindow(in, context);
		super.oldRead(in, context);

		if (leftWindow != null && rightWindow != null) {
			setWindows(leftWindow, rightWindow);
			return this;
		} else
			return leftWindow != null ? leftWindow : rightWindow != null ? rightWindow : null;
	}

	protected void updateWindowItem(RootWindow rootWindow) {
		super.updateWindowItem(rootWindow);
		((SplitWindowItem) getWindowItem()).setParentSplitWindowProperties(rootWindow == null ? SplitWindowItem.emptyProperties : rootWindow
				.getRootWindowProperties().getSplitWindowProperties());
	}

	protected PropertyMap getPropertyObject() {
		return getSplitWindowProperties().getMap();
	}

	protected PropertyMap createPropertyObject() {
		return new SplitWindowProperties().getMap();
	}

	void removeWindowComponent(DockingWindow window) {
		if (window == getLeftWindow())
			splitPane.setLeftComponent(null);
		else
			splitPane.setRightComponent(null);
	}

	void restoreWindowComponent(DockingWindow window) {
		if (window == getLeftWindow())
			splitPane.setLeftComponent(leftWindow);
		else
			splitPane.setRightComponent(rightWindow);
	}

	protected int getChildEdgeDepth(DockingWindow window, Direction dir) {
		return (window == leftWindow ? dir : dir.getOpposite()) == (isHorizontal() ? Direction.RIGHT : Direction.DOWN) ? 0 : super.getChildEdgeDepth(window,
				dir);
	}

	protected DropAction doAcceptDrop(Point p, DockingWindow window) {
		DropAction da = acceptChildDrop(p, window);

		if (da != null)
			return da;

		float f = isHorizontal() ? (float) p.y / getHeight() : (float) p.x / getWidth();

		if (f <= 0.33f) {
			Direction splitDir = isHorizontal() ? Direction.UP : Direction.LEFT;
			return getSplitDropFilter().acceptDrop(new SplitDropInfo(window, this, p, splitDir)) ? split(window, splitDir) : null;
		} else if (f >= 0.66f) {
			Direction splitDir = isHorizontal() ? Direction.DOWN : Direction.RIGHT;
			return getSplitDropFilter().acceptDrop(new SplitDropInfo(window, this, p, splitDir)) ? split(window, splitDir) : null;
		} else {
			return getInteriorDropFilter().acceptDrop(new InteriorDropInfo(window, this, p)) ? createTabWindow(window) : null;
		}
	}

	protected void write(ObjectOutputStream out, WriteContext context, ViewWriter viewWriter) throws IOException {
		out.writeInt(WindowIds.SPLIT);
		viewWriter.writeWindowItem(getWindowItem(), out, context);
		getLeftWindow().write(out, context, viewWriter);
		getRightWindow().write(out, context, viewWriter);
	}

	protected DockingWindow newRead(ObjectInputStream in, ReadContext context, ViewReader viewReader) throws IOException {
		DockingWindow leftWindow = WindowDecoder.decodeWindow(in, context, viewReader);
		DockingWindow rightWindow = WindowDecoder.decodeWindow(in, context, viewReader);

		if (leftWindow != null && rightWindow != null) {
			setWindows(leftWindow, rightWindow);
			return this;
		} else
			return leftWindow != null ? leftWindow : rightWindow != null ? rightWindow : null;
	}

}
